﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TFDP.Common;
using Microsoft.Data.Schema.Sql.SchemaModel;
using Microsoft.VisualStudio.Data.Schema.Package.Project;
using Microsoft.Data.Schema.SchemaModel;
using TFDP.Common.T4;
using System.IO;
using System.ComponentModel.Composition;
using TFDP.Common.Configuration;

namespace TFDP.Processing
{
    [Export(typeof(IGenerator))]
    public class HistoryTableGenerator : IGenerator
    {
        private IProcessor processor;
        private IDatabaseProjectNode project;
        public const string HistoryTableSuffix = "Historie";

        #region IGenerator Members

        public Type SupportedType
        {
            get { return typeof(ISqlTable); }
        }

        public void Initialize(IProcessor processor, Microsoft.VisualStudio.Data.Schema.Package.Project.IDatabaseProjectNode project)
        {
            this.processor = processor;
            this.project = project;

            DatabaseTemplateProcessor.Instance.TemplateDirectory = project.FindTemplateDirectory();
        }

        public IEnumerable<GeneratedArtifact> Generate(Microsoft.Data.Schema.SchemaModel.IModelElement element)
        {
            List<GeneratedArtifact> retvals = new List<GeneratedArtifact>();

            if (element.Node(project).ItemProperty(LoodswezenProperties.GenerateHistoryTableProperty))
            {
                GeneratedArtifact item = GenerateHistoryTable(element);

                if(item != null)
                    retvals.Add(item);
            }

            return retvals;
        }

        #endregion

        private GeneratedArtifact GenerateHistoryTable(IModelElement element)
        {
            ISqlTable sourceTable = element as ISqlTable;
            if (sourceTable == null)
                throw new ArgumentException("Invalid type", "element");


            if (sourceTable.GetName().EndsWith(HistoryTableSuffix))
            {
                Logger.LogWarning("Will not generate a history table because this is a history table");

                return null;
            }

            string elementName = String.Format("{0}.{1}{2}", element.Name.Parts[0], element.Name.Parts[1], HistoryTableSuffix);

            // Perform a lookup of the target element
            ISqlTable historyTable = project.FindEntity<ISqlTable>(elementName);

            // Obtain the properties of the element
            IDatabaseNode node = element.Node(this.project);

            // Execute the template
            string buff = ProcessTemplate<ISqlTable>("HistoryTable.tt", sourceTable, node);

            if (String.IsNullOrEmpty(buff))
            {
                Logger.LogInfo("Processing returned empty result");

                return null;
            }

            // Determine the path
            string path = (historyTable == null) ? ComposeProcedurePath(element.Name, HistoryTableSuffix) : historyTable.PrimarySource.SourceName;

            // Write output to temporary file
            string tempFile = Path.GetTempFileName();
            File.WriteAllText(tempFile, buff);

            // Return the GenerationItem
            return new GeneratedArtifact(tempFile, path, historyTable);
        }

        private string ProcessTemplate<T>(string templateFile, T element, IDatabaseNode node) where T : IModelElement
        {
            return DatabaseTemplateProcessor.Instance.GenerateWithTemplate<T>(templateFile, element, node);
        }

        private string ComposeProcedurePath(ModelIdentifier tableIdentifier, string tableSuffix)
        {
            string format = "{0}\\Schema Objects\\Schemas\\{1}\\Tables\\{2}{3}{4}.table.sql";

            if (processor.Settings.ContainsKey("TablePathFormat"))
            {
                string tablePathFormat = processor.Settings.Get<string>("TablePathFormat");
                if (!String.IsNullOrEmpty(tablePathFormat))
                    format = tablePathFormat;
            }

            return String.Format(
                format,
                project.DirectoryName,
                tableIdentifier.Parts[0],
                String.Empty,
                tableIdentifier.Parts[1],
                tableSuffix
            );
        }

        #region Static T4 Supports
        public static bool HasHistoryTable(ISqlTable table)
        {
            if (table.Name.Parts.Last().EndsWith("Historie"))
                return false;

            ModelIdentifier identifier = table.Model.CreateIdentifier(new string[] { table.Name.Parts[0], table.Name.Parts[1] + "Historie" });

            var matches = table.Model.GetElements<ISqlTable>(identifier, ModelElementQueryFilter.Internal);

            return !(matches == null || matches.Count() == 0);
        }

        public static ISqlTable GetHistoryTable(ISqlTable table)
        {
            if (table.Name.Parts.Last().EndsWith("Historie"))
                throw new InvalidOperationException("The current table is already a history table");

            ModelIdentifier identifier = table.Model.CreateIdentifier(new string[] { table.Name.Parts[0], table.Name.Parts[1] + "Historie" });

            var matches = table.Model.GetElements<ISqlTable>(identifier, ModelElementQueryFilter.Internal);

            if (matches == null || matches.Count() == 0)
                return null;
            else
                return matches[0];
        }
        #endregion
    }
}
